﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
#if NET_2_0
using RaisingStudio.Collections.Generic;
#endif

#if CF_1_0 || CF_2_0
using RaisingStudio.Core;
#endif

namespace RaisingStudio.Data.Linq
{
    public class ExpressionEvaluator : ExpressionVisitor
    {
        private static bool CanBeEvaluated(Expression expression)
        {
            return expression.NodeType != ExpressionType.Parameter;
        }

        private Func<Expression, bool> canBeEvaluated;
        private HashSet<Expression>  candidates;

        public ExpressionEvaluator()
            : this(CanBeEvaluated)
        {
        }

        public ExpressionEvaluator(Func<Expression, bool> canBeEvaluated)
        {
            this.canBeEvaluated = canBeEvaluated;
        }

        public Expression Evaluate(Expression expression)
        {
            this.candidates = new ExpressionNominator(this.canBeEvaluated).Nominate(expression);

            return this.Visit(expression);
        }

        protected override Expression Visit(Expression expression)
        {
            if (expression != null)
            {
                if (candidates.Count > 0)
                {
                    if (this.candidates.Contains(expression))
                    {
                        if (expression.NodeType != ExpressionType.Constant)
                        {
#if CF_1_0 || CF_2_0
                            object value = RaisingStudio.Core.Extensions.DynamicInvoke(((Expression.Lambda(expression)).Compile()), null);
#else
    #if CF_3_5
                                object value = RaisingStudio.Core.Extensions.DynamicInvoke(((Expression.Lambda(expression)).Compile()), null);
    #else
                                object value = (((Expression.Lambda(expression)).Compile()).DynamicInvoke(null));
    #endif
#endif
                            return Expression.Constant(value, expression.Type);
                        }
                        return expression;
                    }
                }

                return base.Visit(expression);
            }
            return expression;
        }
    }
}
